package org.budo.graph.druid.filter.stat;

import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;

import org.budo.support.lang.util.MapUtil;

import com.alibaba.druid.filter.stat.StatFilter;

import lombok.extern.slf4j.Slf4j;

/**
 * @author limingwei
 */
@Slf4j
public class BudoStatFilter extends StatFilter {
    private static final String[] _BUDO_GRAPH_COMMENT = { "/*@SqlGraph*/" };

    /**
     * 为NULL表示没有初始化
     */
    private static Map<String, String> _SOURCE_SQL_TO_MERGED_SQL_MAP = null;

    private static Map<String, Boolean> _SQL_HAS_BUDO_GRAPH_MAP = null;

    public BudoStatFilter() {
        this(false); // 默认不 merge ，不走SQL解析过程 ParameterizedOutputVisitorUtils.parameterize
    }

    public BudoStatFilter(boolean mergeSql) {
        log.info("#33 init BudoStatFilter, mergeSql=" + mergeSql + ", this=" + this);

        this.setMergeSql(mergeSql);

        _SOURCE_SQL_TO_MERGED_SQL_MAP = new WeakHashMap<String, String>();
        _SQL_HAS_BUDO_GRAPH_MAP = new HashMap<String, Boolean>();
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) {
        return super.isWrapperFor(iface) || iface == BudoStatFilter.class;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T unwrap(Class<T> iface) {
        return this.isWrapperFor(iface) ? (T) this : null;
    }

    @Override
    public String mergeSql(String sourceSql, String dbType) {
        String mergedSql = super.mergeSql(sourceSql, dbType);

        _SOURCE_SQL_TO_MERGED_SQL_MAP.put(sourceSql, mergedSql);
        return mergedSql;
    }

    public static Boolean hasBudoGraph(String mergedSql) {
        if (null == mergedSql || null == _SQL_HAS_BUDO_GRAPH_MAP) {
            return false;
        }

        Boolean has = _SQL_HAS_BUDO_GRAPH_MAP.get(mergedSql);
        if (null != has) {
            return has;
        }

        has = _hasBudoGraph(mergedSql);
        _SQL_HAS_BUDO_GRAPH_MAP.put(mergedSql, has);
        return has;
    }

    private static boolean _hasBudoGraph(String mergedSql) {
        if (null == mergedSql || null == _SOURCE_SQL_TO_MERGED_SQL_MAP) {
            return false;
        }

        for (String each : _BUDO_GRAPH_COMMENT) {
            if (mergedSql.trim().startsWith(each)) {
                log.info("#59 mergedSql has " + each + ", mergedSql=" + mergedSql);
                return true;
            }
        }

        String sourceSql = getSourceSqlByMergedSql(mergedSql);
        if (null == sourceSql) {
            return false;
        }

        for (String each : _BUDO_GRAPH_COMMENT) {
            if (sourceSql.trim().startsWith(each)) {
                log.info("#70 sourceSql has " + each + ", mergedSql=" + mergedSql + ", sourceSql=" + sourceSql);
                return true;
            }
        }

        return false;
    }

    private static String getSourceSqlByMergedSql(String mergedSql) {
        try {
            return (String) MapUtil.getKeyByValue_1(_SOURCE_SQL_TO_MERGED_SQL_MAP, mergedSql);
        } catch (Throwable e) {
            log.error("#105 getSourceSqlByMergedSql error, mergedSql=" + mergedSql);
            return null;
        }
    }
}